#include <hd_led.h>
#include <hd_oled_protocol_i2c.h>
#include <hd_oled.h>
#include <delay.h>
#include <pd_adc_ch.h>
#include <stddef.h>
#include <pd_adc.h>
#include <pd_adc_reg.h>
#include <pd_dma.h>
#include <pd_i2c_hd.h>
#include "main.h"

#define ARRAY_SIZE(x)       sizeof(x) / sizeof(x[0])
#define USE_HARDWARE_SPI    1
#define USE_HARDWARE_I2C    1

OLED oled;
uint16_t adcData[4];

extern void SystemClock_Config(void);

void oled_instance(OLED *oled);


int main(void) {
    SystemClock_Config();

    NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

    oled_instance(&oled);

    GPIO adcGPIO[4];
    GPIO_constructor(adcGPIO + 0, GPIOC, LL_GPIO_PIN_0);
    GPIO_constructor(adcGPIO + 1, GPIOC, LL_GPIO_PIN_1);
    GPIO_constructor(adcGPIO + 2, GPIOC, LL_GPIO_PIN_2);
    GPIO_constructor(adcGPIO + 3, GPIOC, LL_GPIO_PIN_3);

    uint32_t ranks[] = {LL_ADC_REG_RANK_1, LL_ADC_REG_RANK_2, LL_ADC_REG_RANK_3, LL_ADC_REG_RANK_4,};

    uint32_t channels[] = {LL_ADC_CHANNEL_10, LL_ADC_CHANNEL_11, LL_ADC_CHANNEL_12, LL_ADC_CHANNEL_13,};

    ADCChannel adcChannel;
    ADCChannel_constructor(&adcChannel, adcGPIO, ARRAY_SIZE(adcGPIO), ranks, ARRAY_SIZE(ranks),
                           channels, ARRAY_SIZE(channels));


    ADCBase adcBase;
    ADCBase_constructor(&adcBase, ADC2, &adcChannel, LL_ADC_SEQ_SCAN_ENABLE);

    // ADC scan mode(ADC_CR1_SCAN, Bit8)
    ADCRegular adcRegular;
    ADCRegular_constructor(&adcRegular, &adcBase,
                           LL_ADC_REG_TRIG_SOFTWARE,          // Software Trigger
                           LL_ADC_REG_SEQ_SCAN_ENABLE_4RANKS, // Scan Sequencer length
                           LL_ADC_REG_CONV_CONTINUOUS,        // Single or Continuous(ADC_CR2_CONT)
                           LL_ADC_REG_DMA_TRANSFER_UNLIMITED);

    ADCRegular_init(&adcRegular);

    DMA dma;
    DMA_constructor(&dma, DMA2, (uint32_t)&ADC2->DR, (uint32_t)adcData,
                    4, LL_DMA_CHANNEL_1, LL_DMA_STREAM_2);
    dma.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY;
    dma.Mode = LL_DMA_MODE_CIRCULAR;
    dma.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
    dma.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
    dma.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
    dma.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
    DMA_init(&dma);

    ADCRegular_start(&adcRegular);

    OLED_printf(&oled, 0, 00, "AD0:           V");
    OLED_printf(&oled, 0, 16, "AD1:           V");
    OLED_printf(&oled, 0, 32, "AD2:           V");
    OLED_printf(&oled, 0, 48, "AD3:           V");

    while (1) {
        OLED_printf(&oled, 32, 00, "%4d", adcData[0]);
        OLED_showFloat(&oled, 88, 00, (float)adcData[0] / 4096 * 3.3, 3);

        OLED_printf(&oled, 32, 16, "%4d", adcData[1]);
        OLED_showFloat(&oled, 88, 16, (float)adcData[1] / 4096 * 3.3, 3);

        OLED_printf(&oled, 32, 32, "%4d", adcData[2]);
        OLED_showFloat(&oled, 88, 32, (float)adcData[2] / 4096 * 3.3, 3);

        OLED_printf(&oled, 32, 48, "%4d", adcData[3]);
        OLED_showFloat(&oled, 88, 48, (float)adcData[3] / 4096 * 3.3, 3);

        OLED_refresh(&oled);

        delay_ms(100);
    }
}


void oled_instance(OLED *oled) {
    // I2C gpio init
    static GPIO oledSckGPIO, oledSdaGPIO;
    GPIO_constructor(&oledSckGPIO, GPIOB, LL_GPIO_PIN_10); // I2Cx_SCK
    GPIO_constructor(&oledSdaGPIO, GPIOB, LL_GPIO_PIN_11); // I2Cx_SDA

#ifdef USE_HARDWARE_I2C
    static I2CHardware i2c2;
    I2CHardware_constructor(&i2c2, I2C2, &oledSckGPIO, &oledSdaGPIO);
#elif USE_SIMULATE_I2C
    static I2CSimulate i2c2;
    I2CSimulate_constructor(&i2c2, &oledSckGPIO, &oledSdaGPIO);
#endif
    I2C_init((I2C *)&i2c2);

    // OLED protocol of I2C
    static OLEDProtocolI2C oledProtocolI2c;
    OLEDProtocolI2C_constructor(&oledProtocolI2c, (I2C *)&i2c2);

    // OLED font instance.
    static OLEDFont oledFont;
    OLEDFont_contructor(&oledFont, NULL);

    // OLED instance of 4-PIN(I2C)
    OLED_constructor(oled, (OLEDProtocol *)&oledProtocolI2c, &oledFont);

    OLED_init(oled);

    OLED_clear(oled);
    OLED_refresh(oled);
}
